#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include <string.h>
#include <strings.h>
#include <string>
#include "InterAddr.hpp"

#define defaultfd -1
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};

class UdpServer
{
public:
    UdpServer(/*const std::string &ip, */uint16_t port) : _sockfd(defaultfd), _port(port)/*_ip(ip),*/, _isrunning(false)
    {
    }
    void InitServer()
    {
        // 1. 创建udp socket套接字,socket套接字其实在liunx中其实是一个文件，所以返回的是一个文件描述符
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(RATAL, "socket error, %s, %d", strerror(errno), errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "stock create sucess, sockfd: %d\n", _sockfd);

        // 2.0 填充struct sockaddr_in结构
        struct sockaddr_in local;     // struct sockaddr_int 是系统提供的数据类型，local是变量，在用户栈上开辟空间
        bzero(&local, sizeof(local)); // bzero 清空函数
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // port要经过网络传输给对面，先到网络，_port要转化成网络序列即：大端
        // a. 将字符串风格的点分十进制ip地址，转化为4字节的风格的ip
        // b. 主机序列转化为网络序列
        // in_addr_t inet_addr()函数同时可以完成 a & b
        //local.sin_addr.s_addr = inet_addr(_ip.c_str());
        local.sin_addr.s_addr = INADDR_ANY;//直接设置为0，这样就可以接受任意的来自不同的ip主机
 
        // 2. bind 绑定sockfd和网络信息（IP + port）, 因为以上信息都是在用户栈上的操作并没在操作系统内进行，所以要将其bind到操作系统中
        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(RATAL, "bind error, %s, %d", strerror(errno), errno);
            exit(BIND_ERROR);
        }
        LOG(INFO, "socket bind sucess\n");
    }
    void Start()
    {
        // 服务器一直是在运行的，知道管理者主动退出，死循环
        //
        while (true)
        {
            // sleep(1);
            // LOG(INFO, "server is running....\n");

            // 1. 我们先要让serve收到数据
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer); // 这个必须初始化为sizeof(peer)
            // 读数据
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            if (n > 0)
            {
                buffer[n] = 0;
                InterAddr addr(peer);
                LOG(INFO, "get massage from[%s:%d]: %s\n",addr.Ip().c_str(), addr.Port(), buffer);
                // 2. 将收到的数据，发回给对方
                sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr*)&peer, len);
            }
        }
        _isrunning = true;

    }
    ~UdpServer()
    {
    }

private:
    int _sockfd;
    //std::string _ip;
    uint16_t _port;
    bool _isrunning;
};